package cn.sofwin.core.utils;

import com.alibaba.druid.pool.DruidDataSourceFactory;
import org.apache.commons.dbutils.*;
import org.apache.commons.dbutils.handlers.BeanHandler;
import org.apache.commons.dbutils.handlers.BeanListHandler;
import org.apache.commons.dbutils.handlers.ScalarHandler;

import javax.sql.DataSource;
import java.io.InputStream;
import java.math.BigInteger;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.List;
import java.util.Properties;

/**
 * JDBC 工具类
 * @author LetCode
 * @since 1.0
 */
public class JdbcUtils {

    /** 公开的行处理器，用于将数据库表中下划线命名映射到类中属性的小驼峰命名规则 */
    public static RowProcessor beanRowProcessor = null;

    /** 私有的静态数据库连接池对象 */
    private static DataSource dataSource = null;

    /** 私有的静态结果集处理器，用于处理插入操作后获取最新的ID值 */
    private static ResultSetHandler<Integer> idHandler;

    static {
        // 在静态代码块中初始化连接池对象
        Properties po = new Properties();
        try(InputStream is = JdbcUtils.class.getClassLoader().getResourceAsStream("db.properties")){
            po.load(is);
            dataSource = DruidDataSourceFactory.createDataSource(po);

            // 为了增强兼容性，手动装载 MySQL 驱动
            Class.forName("com.mysql.cj.jdbc.Driver");

            // 初始化 beanRowProcessor
            beanRowProcessor = new BasicRowProcessor(new GenerousBeanProcessor());

            // 初始化 idHandler
            idHandler = rs -> {
                rs.next();
                BigInteger bi = (BigInteger) rs.getObject(1);
                return bi.intValue();
            };
        }catch (Exception e){
            e.printStackTrace();
        }
    }

    /**
     * 执行插入语句
     * @param sql       SQL语句
     * @param params    SQL语句中对应的参数
     * @return
     */
    public static int insert(String sql, Object... params){
        QueryRunner queryRunner = new QueryRunner(dataSource);
        Integer id = null;
        try {
            id =  queryRunner.insert(sql, idHandler, params);
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }
        return id;
    }

    /**
     * 执行更新语句（更行或删除）
     * @param sql       SQL语句
     * @param params    SQL语句中对应的参数
     * @return
     */
    public static int update(String sql, Object... params){
        Connection conn = getConnection();
        if(conn == null) return 0;

        QueryRunner queryRunner = new QueryRunner(dataSource);
        int rows = 0;
        try {
            // 关闭事务的自动提交
            conn.setAutoCommit(false);

            // 执行业务代码，可以执行多条SQL语句
            rows = queryRunner.update(sql, params);

            // 手动提交事务
            conn.commit();
            // 自己用完了之后将连接设置为原始状态
            conn.setAutoCommit(true);
        } catch (SQLException throwables) {
            throwables.printStackTrace();
            try {
                // 手动回滚事务
                conn.rollback();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        } finally {
            close(conn);
        }
        return rows;
    }

    /**
     * 查询单个值结果
     * @param sql
     * @param params
     * @param <T>
     * @return
     */
    public static <T> T query(String sql, Object... params){
        QueryRunner queryRunner = new QueryRunner(dataSource);
        T value = null;
        try {
            value = queryRunner.query(sql, new ScalarHandler<>(), params);
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }
        return value;
    }

    /**
     * 查询当个对象（将单行多列的结果封装成类的对象）
     * @param clazz     对象的类型
     * @param sql       SQL语句
     * @param params    SQL语句中的参数
     * @param <T>
     * @return
     */
    public static <T> T queryForObject(Class<? extends T> clazz, String sql, Object... params){
        QueryRunner queryRunner = new QueryRunner(dataSource);
        T obj = null;
        try {
            obj = queryRunner.query(sql, new BeanHandler<>(clazz, beanRowProcessor), params);
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }
        return obj;
    }

    /**
     * 查询当个对象（将单行多列的结果封装成类的对象）
     * @param sql       SQL语句
     * @param handler   用户自定义的结果集处理器
     * @param params    SQL语句中的参数
     * @param <T>
     * @return
     */
    public static <T> T queryForObject(String sql, ResultSetHandler<T> handler, Object... params){
        QueryRunner queryRunner = new QueryRunner(dataSource);
        T obj = null;
        try {
            obj = queryRunner.query(sql, handler, params);
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }
        return obj;
    }

    /**
     * 查询对象列表（将多行多列的结果集封装对象列表）
     * @param clazz     对象的类型
     * @param sql       SQL语句
     * @param params    SQL语句中的参数
     * @param <T>
     * @return
     */
    public static <T> List<T> queryForList(Class<? extends T> clazz, String sql, Object... params){
        QueryRunner queryRunner = new QueryRunner(dataSource);
        List<T> list = null;
        try {
            list = queryRunner.query(sql, new BeanListHandler<>(clazz, beanRowProcessor), params);
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }
        return list;
    }

    /**
     * 查询对象列表（将多行多列的结果集封装对象列表）
     * @param sql       SQL语句
     * @param handler   用户自定义的结果集处理器
     * @param params    SQL语句中的参数
     * @param <T>
     * @return
     */
    public static <T> List<T> queryForList(String sql, ResultSetHandler<List<T>> handler, Object... params){
        QueryRunner queryRunner = new QueryRunner(dataSource);
        List<T> list = null;
        try {
            list = queryRunner.query(sql, handler, params);
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }
        return list;
    }

    /**
     * 获取数据库链接
     * @return
     */
    public static Connection getConnection(){
        Connection conn = null;

        try {
            conn = dataSource.getConnection();
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }
        return conn;
    }

    /**
     * 关闭结果集
     * @param rs
     */
    public static void close(ResultSet rs){
        if(rs != null){
            try {
                rs.close();
            } catch (SQLException throwables) {
                throwables.printStackTrace();
            }
        }
    }

    /**
     * 关闭操作语句
     * @param stmt
     */
    public static void close(Statement stmt){
        if(stmt != null){
            try {
                stmt.close();
            } catch (SQLException throwables) {
                throwables.printStackTrace();
            }
        }
    }

    /**
     * 关闭数据库连接
     * @param conn
     */
    public static void close(Connection conn){
        if(conn != null){
            try {
                conn.close();
            } catch (SQLException throwables) {
                throwables.printStackTrace();
            }
        }
    }
}
